package com.zimi.core.aspect;

import com.alibaba.fastjson.JSONObject;
import com.zimi.mapper.SysOperLogMapper;
import com.zimi.model.SysOperLog;
import com.zimi.util.UserUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Slf4j
@Aspect
@Component
public class ControllerAspect {

    private final ThreadLocal<String> methodDescribe = new ThreadLocal<>();//方法返回参数
    @Autowired
    private SysOperLogMapper logMapper;
    private boolean neeLog;//是否需要写入日志
    private SysOperLog operLog;

    @Pointcut("execution(public * com.zimi.controller..*.*(..))")
    public void webLog() {
    }


    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        recordMethodLog(joinPoint, getRequestContext());
    }


    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        methodDescribe.get();
        if (neeLog) {
            operLog.setOperMsg(JSONObject.toJSONString(ret));
            operLog.updateById();
        }
        methodDescribe.remove();
    }

    private void recordMethodLog(JoinPoint joinPoint, HttpServletRequest request) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        neeLog = false;
        String apiName = signature.getDeclaringTypeName().toLowerCase();
        if (apiName.matches("^com\\.zimi.*sys[a-z\\-/]+api$")) {
            if (!signature.getName().toLowerCase().matches("^\\w?(sel|list|page|find|get|all)\\w*")) {
                operLog = new SysOperLog()
                        .setMethod(request.getMethod())
                        .setUserAgent(request.getHeader("user-agent"))
                        .setOperName(methodDoc(method))
                        .setOperUserId(UserUtil.getUser().getId())
                        .setOperUserName(UserUtil.getUser().getUserName())
                        .setOperAddr(request.getRemoteAddr())
                        .setOperParam(parseArgs(joinPoint))
                        .setOperUrl(request.getRequestURL().toString())
                        .setOperTime(LocalDateTime.now());
                operLog.insert();
                neeLog = true;
            }
        }
    }

    private String methodDoc(Method method) {
        String val = null;
        try {
            ApiOperation api;
            api = method.getAnnotation(ApiOperation.class);
            val = api.value();
        } catch (Exception e) {
            val = method.getName();
        }
        return val;
    }

    private HttpServletRequest getRequestContext() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        return attributes.getRequest();
    }

    private String parseArgs(JoinPoint joinPoint) {
        StringBuilder sb = new StringBuilder();
        Object[] args = joinPoint.getArgs();
        if (args != null) {
            for (Object arg : args) {
                if (arg instanceof BindingResult ||
                        arg instanceof HttpServletRequest ||
                        arg instanceof HttpServletResponse ||
                        arg instanceof MultipartFile ||
                        arg instanceof MultipartFile[]) {
                    continue;
                }
                sb.append(" ");
                sb.append(JSONObject.toJSONString(arg));
            }
        }
        return sb.toString();
    }
}
